
/*
 *  This program is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation, either version 3 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 * SPDX-License-Identifier: GPL-3.0+
 * License-Filename: LICENSE
 *
 */

#include "config.h"

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "splay-tree.h"
#include "main.h"
#include "parse.h"
#include "parsedot.h"
#include "uniqstring.h"
#include "uniqnode.h"
#include "nes.h"
#include "mem.h"
#include "color.h"
#include "options.h"

/* is a node definition with optional options */
int is_a_dotne_node(struct usubg *subg, char *fname, char *port, char *compass)
{
	char *tmps = NULL;
	int tmpi = 0;
	int labelset = 0;
	int fillcolor = 0x00ffffff;	/* white node background color */
	int fillcolorn = 0;
	int fillcolor_set = 0;
	int fcolor = 0x00;	/* black text color */
	char *flabel = NULL;
	char *tmpfontsize = NULL;
	char *tmpfontname = NULL;
	char *tmpurl = NULL;
	int tmpurl_set = 0;
	int tmpshape = 0;
	int tmpshape_set = 0;
	struct unode *un = NULL;
	char *bcolorstr = NULL;
	int bcolorn = 0;
	int bcolor = 0;
	int bcolor_set = 0;
	char *csstr = NULL;
	int cscode = 0;
	int cscode_set = 0;
	char *styles = NULL;
	int style_set = 0;
	int stylefilled_set = 0;

	/* check node name */
	if (fname == NULL) {
		parser_error_string = "nil node name at node definition";
		return (0);
	}
	if (strlen(fname) == 0) {
		parser_error_string = "empty node name at node definition";
		return (0);
	}
	/* optional node port */
	if (port) {
		/* */
	}
	/* optional compass point */
	if (compass) {
		/* */
	}
	/* */
	labelset = 0;
	fillcolor = 0x00ffffff;
	flabel = NULL;
	tmpshape = 0;		/* default shape is 0 */
	tmpshape_set = 0;
	tmpurl_set = 0;
	tmpurl = NULL;
	bcolorstr = NULL;
	bcolor = 0;
	bcolor_set = 0;
	bcolorn = 0;
	csstr = NULL;
	cscode = 0;
	cscode_set = 0;
	fillcolorn = 0;
	fillcolor_set = 0;
	styles = NULL;
	style_set = 0;
	stylefilled_set = 0;

	/*
	 * parsed a node now and check for optional [] parameters
	 */
	if (token == DOT_BRACKETO) {
		/* keep lexing the options for node */
		for (;;) {
			token = dot_lex();
			if (token == 0) {
				parser_error_string = "un-expected end-of-file at node option";
				return (0);
			}
			if (token == DOT_SEMIC) {
				/* skip optional semicomma */
				token = dot_lex();
			}
			if (token == DOT_COMMA) {
				/* skip optional comma */
				token = dot_lex();
			}
			if (token == DOT_BRACKETC) {
				/* end of options */
				break;
			}

			/* keep on reading the node options
			 * color colorscheme comment distortion fillcolor fixedsize fontcolor fontname
			 * fontsize group height id image imagescale label labelloc layer margin nojustify
			 * orientation penwidth peripheries pin pos rects regular root samplepoints shape
			 * shapefile showboxes sides skew sortv style target tooltip vertices width z
			 */

			/*
			 * here start the options
			 */
			if (token == DOT_CHAR) {
				/* 3d-position, depreciated, double number z opt */
				if (strcmp(lastchar, "z") == 0) {
					if (token != DOT_IS) {
						parser_error_string = "expected = at node z";
						return (1);
					}
					token = dot_lex();
					if (token == DOT_STRING) {
						/* */
					} else if (token == DOT_NUM) {
						/* */
					} else {
						parser_error_string = "expected number at node z";
						return (1);
					}
				} else {
					/* unknown */
					if (token != DOT_IS) {
						parser_error_string = "expected = at node unknown-option";
						return (1);
					}
					token = dot_lex();
				}
			} else if (token == DOT_ID) {
				/* 1) begin of node settings */
				/* node url, URL="http://www.fsf.org" , same as href attribute */
				if (strcmp(lastid, "URL") == 0) {
					token = dot_lex();
					if (token != DOT_IS) {
						parser_error_string = "expected = at node URL";
						return (1);
					}
					token = dot_lex();
					if (token == DOT_STRING) {
						/* laststring has the url */
						tmpurl = laststring;
					} else if (token == DOT_ID) {
						tmpurl = lastid;
					} else {
						parser_error_string = "expected url-string at node URL";
						return (1);
					}
					/* check for "" */
					if (strlen(tmpurl) == 0) {
						tmpurl = NULL;
					}
					tmpurl_set = 1;
				} else if (strcmp(lastid, "area") == 0) {
					/* node area, area=2.0 (double), select preferred node area for patchwork */
					token = dot_lex();
					if (token != DOT_IS) {
						parser_error_string = "expected = at node area";
						return (1);
					}
					token = dot_lex();
					if (token == DOT_STRING) {
						/* "2.0" */
					} else if (token == DOT_NUM) {
						/* 2.0 */
					} else {
						parser_error_string = "expected double number at node area";
						return (1);
					}
				} else if (strcmp(lastid, "color") == 0) {
					/* if fillcolor is not set, node is filled with color setting.
					 * if fillcolor is set, color is used for border color.
					 */
					/* color of the graphic of a node or a colorlist for a gradient */
					token = dot_lex();
					if (token != DOT_IS) {
						parser_error_string = "expected = at node color";
						return (1);
					}
					token = dot_lex();
					/* color arg can be a color list, number, id, string */
					if (token == DOT_NUM) {
						/* colorscheme number */
						bcolorn = atoi(lastnum);	/* fixme should do better here */
						/* colorscheme color earlier defined? */
						if (cscode_set) {
							bcolor = colornscode(bcolorn, cscode);
						} else {
							if (subg == NULL) {
								bcolor = colornscode(bcolorn, nd_cs);
							} else {
								bcolor = colornscode(bcolorn, subg->nd_cs);
							}
						}
						if (bcolor == -1) {
							/* black node border color */
							bcolor = 0;
							bcolor_set = 0;
						} else {
							bcolor_set = 1;
						}
					} else {
						if (token == DOT_STRING) {
							bcolorstr = laststring;
						} else if (token == DOT_ID) {
							bcolorstr = lastid;
						} else {
							parser_error_string = "expected colorname at node color";
							return (1);
						}
						bcolor = colorcode(bcolorstr);
						if (bcolor == -1) {
							bcolorstr = NULL;
							bcolor_set = 0;
							bcolor = 0x00ffffff;
						} else {
							bcolor_set = 1;
						}
					}
				} else if (strcmp(lastid, "colorscheme") == 0) {
					/* color scheme to use with numbered colors, see bison graph data */
					token = dot_lex();
					if (token != DOT_IS) {
						parser_error_string = "expected = at node colorscheme";
						return (1);
					}
					token = dot_lex();
					if (token == DOT_STRING) {
						csstr = laststring;
					} else if (token == DOT_ID) {
						csstr = lastid;
					} else {
						parser_error_string = "expected name at node colorscheme";
						return (1);
					}
					/* code of colorscheme or 0 if not found */
					cscode = colorschemecode(csstr);
					if (cscode) {
						cscode_set = 1;
					}
				} else if (strcmp(lastid, "comment") == 0) {
					/* comment inserted into output, depends on device too */
					token = dot_lex();
					if (token != DOT_IS) {
						parser_error_string = "expected = at node comment";
						return (1);
					}
					token = dot_lex();
					if (token == DOT_STRING) {
					} else {
						parser_error_string = "expected string at node comment";
						return (1);
					}
				} else if (strcmp(lastid, "distortion") == 0) {
					/* distortion factor for polygon shape, +/- double number distortion="0.936354" */
					token = dot_lex();
					if (token != DOT_IS) {
						parser_error_string = "expected = at node distortion";
						return (1);
					}
					token = dot_lex();
					if (token == DOT_STRING) {
						/* */
					} else if (token == DOT_NUM) {
					} else {
						parser_error_string = "expected double number at node distortion";
						return (1);
					}
				} else if (strcmp(lastid, "fillcolor") == 0) {
					/* background color of node or white if unknown */
					token = dot_lex();
					if (token != DOT_IS) {
						parser_error_string = "expected = at node fillcolor";
						return (1);
					}
					token = dot_lex();
					if (token == DOT_NUM) {
						fillcolorn = atoi(lastnum);	/* fixme should do better here XXX strtod() */
						/* colorscheme color earlier defined? */
						if (cscode_set) {
							fillcolor = colornscode(fillcolorn, cscode);
						} else {
							if (subg == NULL) {
								fillcolor = colornscode(fillcolorn, nd_cs);
							} else {
								fillcolor = colornscode(fillcolorn, subg->nd_cs);
							}
						}
						/* ignore unknown color */
						if (fillcolor == -1) {
							fillcolor_set = 0;
						} else {
							fillcolor_set = 1;
						}
					} else {
						if (token == DOT_STRING) {
							fillcolor = colorcode(laststring);
							if (fillcolor == -1) {
								/* white node background color */
								fillcolor_set = 0;
							} else {
								fillcolor_set = 1;
							}
						} else if (token == DOT_ID) {
							/* GCC7 uses lightgrey but that should be LightGrey */
							fillcolor = colorcode(lastid);
							if (fillcolor == -1) {
								/* white node background color */
								fillcolor_set = 0;
							} else {
								fillcolor_set = 1;
							}
						} else {
							parser_error_string = "expected color at node fillcolor option";
							return (0);
						}
					}
				} else if (strcmp(lastid, "fixedsize") == 0) {
					/* false/true/any/shape, size of node and the label text */
					token = dot_lex();
					if (token != DOT_IS) {
						parser_error_string = "expected = at node fixedsize";
						return (1);
					}
					token = dot_lex();
				} else if (strcmp(lastid, "fontcolor") == 0) {
					/* color for text */
					token = dot_lex();
					if (token != DOT_IS) {
						parser_error_string = "expected = at node fontcolor";
						return (1);
					}
					token = dot_lex();
					if (token == DOT_STRING) {
						fcolor = colorcode(laststring);
						if (fcolor == -1) {
							/* black text color */
							fcolor = 0x00;
						}
					} else if (token == DOT_ID) {
						fcolor = colorcode(laststring);
						if (fcolor == -1) {
							/* black text color */
							fcolor = 0x00;
						}
					} else if (token == DOT_NUM) {
						/* number related to colorscheme */
						fcolor = 0x00;
					} else {
						/* unknown */
						if (1) {
							/* black */
							fcolor = 0x00;
						} else {
							parser_error_string = "expected color at node fontcolor option";
							return (0);
						}
					}
				} else if (strcmp(lastid, "fontname") == 0) {
					/* name of font to use for text */
					token = dot_lex();
					if (token != DOT_IS) {
						parser_error_string = "expected = at node fontname";
						return (1);
					}
					token = dot_lex();
					if (token == DOT_STRING) {
						tmpfontname = laststring;
					} else if (token == DOT_ID) {
						tmpfontname = lastid;
					} else if (token == DOT_CHAR) {
						/* fontname can be a single char */
						tmpfontname = lastchar;
					} else {
						parser_error_string = "expected string or id at node fontname";
						return (1);
					}
					if (strlen(tmpfontname) == 0) {
						tmpfontname = NULL;
					}
				} else if (strcmp(lastid, "fontsize") == 0) {
					/* size of font for text */
					token = dot_lex();
					if (token != DOT_IS) {
						parser_error_string = "expected = at node fontsize";
						return (1);
					}
					token = dot_lex();
					if (token == DOT_STRING) {
						tmpfontsize = laststring;
					} else if (token == DOT_NUM) {
						tmpfontsize = lastnum;
					} else {
						parser_error_string = "expected string or number at node fontsize";
						return (1);
					}
					/* use gtk default at fontsize=0 or fontsize="" */
					if (strlen(tmpfontsize) == 0) {
						tmpfontsize = NULL;
					} else if (atoi(tmpfontsize) == 0) {	/* should be strtol() */
						tmpfontsize = NULL;
					} else {	/* assume correct fontsize */
					}
				} else if (strcmp(lastid, "gradientangle") == 0) {
					/* angle of fill, int number */
					token = dot_lex();
					if (token != DOT_IS) {
						parser_error_string = "expected = at node gradientangle";
						return (1);
					}
					token = dot_lex();
					if (token == DOT_NUM) {
						/* int, default 0 */
					} else {
						parser_error_string = "expected integer number at node gradientangle";
						return (1);
					}
				} else if (strcmp(lastid, "group") == 0) {
					/* endpoint of edge grouping, string */
					token = dot_lex();
					if (token != DOT_IS) {
						parser_error_string = "expected = at node group";
						return (1);
					}
					token = dot_lex();
					if (token == DOT_STRING) {
						/* */
					} else if (token == DOT_ID) {
						/* */
					} else {
						parser_error_string = "expected string or id at node group";
						return (1);
					}
				} else if (strcmp(lastid, "height") == 0) {
					/* min. height of node, double */
					token = dot_lex();
					if (token != DOT_IS) {
						parser_error_string = "expected = at node height";
						return (1);
					}
					token = dot_lex();
					if (token == DOT_STRING) {
						/* */
					} else if (token == DOT_NUM) {
						/* */
					} else {
						parser_error_string = "expected number at node height";
						return (1);
					}
				} else if (strcmp(lastid, "href") == 0) {
					/* same as URL, http esc string */
					token = dot_lex();
					if (token != DOT_IS) {
						parser_error_string = "expected = at node href";
						return (1);
					}
					token = dot_lex();
					if (token == DOT_STRING) {
						/* laststring has the url */
						tmpurl = laststring;
					} else if (token == DOT_ID) {
						tmpurl = lastid;
					} else {
						parser_error_string = "expected url-string at node href";
						return (1);
					}
					/* check for "" */
					if (strlen(tmpurl) == 0) {
						tmpurl = NULL;
					}
					tmpurl_set = 1;

				} else if (strcmp(lastid, "id") == 0) {
					/* uniq id for node optionally supplied, escstring */
					token = dot_lex();
					if (token != DOT_IS) {
						parser_error_string = "expected = at node id";
						return (1);
					}
					token = dot_lex();
					if (token == DOT_STRING) {
						/* */
					} else if (token == DOT_ID) {
						/* */
					} else if (token == DOT_NUM) {
						/* */
					} else if (token == DOT_CHAR) {
						/* */
					} else {
						parser_error_string = "expected uniq id at node id";
						return (1);
					}
				} else if (strcmp(lastid, "image") == 0) {
					/* image to display inside a node */
					token = dot_lex();
					if (token != DOT_IS) {
						parser_error_string = "expected = at node image";
						return (1);
					}
					token = dot_lex();
					if (token == DOT_STRING) {
						/* */ } else {
						parser_error_string = "expected image string at node image";
						return (1);
					}
				} else if (strcmp(lastid, "imagepos") == 0) {
					/* how image is positioned inside node shape */
					token = dot_lex();
					if (token != DOT_IS) {
						parser_error_string = "expected = at node imagepos";
						return (1);
					}
					token = dot_lex();
					if (token == DOT_STRING) {
						/* */
					} else if (token == DOT_ID) {
						/* */
					} else {
						parser_error_string = "expected position at node imagepos";
						return (1);
					}
					/* position is: tl, tc, tr, ml, mc, mr, bl, bc, br, default mc */
				} else if (strcmp(lastid, "imagescale") == 0) {
					/* scale image true/false */
					token = dot_lex();
					if (token != DOT_IS) {
						parser_error_string = "expected = at node imagescale";
						return (1);
					}
					token = dot_lex();
				} else if (strcmp(lastid, "label") == 0) {
					/* text of node */
					token = dot_lex();
					if (token != DOT_IS) {
						parser_error_string = "expected = at node label";
						return (1);
					}
					token = dot_lex();
					if (token == DOT_STRING) {
						flabel = laststring;
						/* check for "" */
						if (strlen(laststring) == 0) {
							/* because of forgotten reasons a "" is replaced with a space */
							flabel = uniqstring((char *)" ");
						}
						/* option to use name of node instead of label text */
						if (option_nodenames) {
							flabel = NULL;
						}
						if (flabel) {
							labelset = 1;
						}
					} else if (token == DOT_ID) {
						flabel = lastid;
						if (strlen(lastid) == 0) {
							flabel = NULL;
						}
						if (option_nodenames) {
							flabel = NULL;
						}
						if (flabel) {
							labelset = 2;
						}
					} else if (token == DOT_NUM) {
						flabel = lastnum;
						if (strlen(lastnum) == 0) {
							flabel = NULL;
						}
						if (option_nodenames) {
							flabel = NULL;
						}
						if (flabel) {
							labelset = 3;
						}
					} else if (token == DOT_HTML) {
						/* html string is interpreted, just as record string */
						flabel = lasthtml;
						labelset = 4;
					} else if (token == DOT_CHAR) {
						flabel = lastchar;
						labelset = 5;
					} else {
						parser_error_string = "expected string at node label option";
						return (0);
					}
				} else if (strcmp(lastid, "labelloc") == 0) {
					/* vertical placement of label text */
					token = dot_lex();
					if (token != DOT_IS) {
						parser_error_string = "expected = at node labelloc";
						return (1);
					}
					token = dot_lex();
					if (token == DOT_STRING) {
						/* */
					} else if (token == DOT_CHAR) {
						/* t b c allowed for a node */
					} else {
						parser_error_string = "expected t/b/c at node labelloc";
						return (1);
					}
				} else if (strcmp(lastid, "layer") == 0) {
					/* layer in which node is present */
					token = dot_lex();
					if (token != DOT_IS) {
						parser_error_string = "expected = at node layer";
						return (1);
					}
					token = dot_lex();
					if (token == DOT_STRING) {
						/* */
					} else {
						parser_error_string = "expected layer-list at node layer";
						return (1);
					}
				} else if (strcmp(lastid, "margin") == 0) {
					/* space around node text label, double, example "0.02,0" */
					token = dot_lex();
					if (token != DOT_IS) {
						parser_error_string = "expected = at node margin";
						return (1);
					}
					token = dot_lex();
					/* margin with optional x,y direction in pixels around label text %lf,%lf */
					if (token == DOT_STRING) {
						/* only x spacing or x,y spacing */
					} else if (token == DOT_NUM) {
						/* only x spacing */
					} else {
						parser_error_string = "expected numbers at node margin";
						return (1);
					}
				} else if (strcmp(lastid, "nojustify") == 0) {
					/* justify label text true/false */
					token = dot_lex();
					if (token != DOT_IS) {
						parser_error_string = "expected = at node nojustify";
						return (1);
					}
					token = dot_lex();
				} else if (strcmp(lastid, "ordering") == 0) {
					/* in/out edge ordering, string, in/out */
					token = dot_lex();
					if (token != DOT_IS) {
						parser_error_string = "expected = at node ordening";
						return (1);
					}
					token = dot_lex();
					if (token == DOT_STRING) {
						/* */
					} else {
						parser_error_string = "expected string at node ordening";
						return (1);
					}
				} else if (strcmp(lastid, "orientation") == 0) {
					/* rotate shape in double angle, 0..360 */
					token = dot_lex();
					if (token != DOT_IS) {
						parser_error_string = "expected = at node orientation";
						return (1);
					}
					token = dot_lex();
					if (token == DOT_STRING) {
						/* */
					} else if (token == DOT_NUM) {
						/* */
					} else {
						parser_error_string = "expected number at node orientation";
						return (1);
					}
				}	/* XXX missing pencolor here */
				else if (strcmp(lastid, "penwidth") == 0) {
					/* double number to draw thickness of lines */
					token = dot_lex();
					if (token != DOT_IS) {
						parser_error_string = "expected = at node penwidth";
						return (1);
					}
					token = dot_lex();
					if (token == DOT_STRING) {
						/* */
					} else if (token == DOT_NUM) {
						/* */
					} else {
						parser_error_string = "expected number at node penwidth";
						return (1);
					}
				} else if (strcmp(lastid, "peripheries") == 0) {
					/* number of peripheries in a shape, int */
					token = dot_lex();
					if (token != DOT_IS) {
						parser_error_string = "expected = at node peripheries";
						return (1);
					}
					token = dot_lex();
					if (token == DOT_STRING) {
						/* */
					} else if (token == DOT_NUM) {
						/* */
					} else {
						parser_error_string = "expected integer number at node peripheries";
						return (1);
					}
				} else if (strcmp(lastid, "pin") == 0) {
					/* pin position, boolean, not dot but neato */
					token = dot_lex();
					if (token != DOT_IS) {
						parser_error_string = "expected = at node pin";
						return (1);
					}
					token = dot_lex();
					parser_error_string = "unexpected node pin is not dot but neato";
				} else if (strcmp(lastid, "pos") == 0) {
					/* position of node in points */
					token = dot_lex();
					if (token != DOT_IS) {
						parser_error_string = "expected = at node pos";
						return (1);
					}
					token = dot_lex();
					if (token == DOT_STRING) {
						/* */
					} else if (token == DOT_NUM) {
						/* */
					} else {
						parser_error_string = "expected point at node pos";
						return (1);
					}
				} else if (strcmp(lastid, "rects") == 0) {
					/* rectangles for fields in record in points, %f,%f,%f,%f */
					token = dot_lex();
					if (token != DOT_IS) {
						parser_error_string = "expected = at node rects";
						return (1);
					}
					token = dot_lex();
					if (token == DOT_STRING) {
						/* */
					} else if (token == DOT_NUM) {
						/* */
					} else {
						parser_error_string = "expected point at node rects";
						return (1);
					}
				} else if (strcmp(lastid, "regular") == 0) {
					/* force polygon to be regular, true/false */
					token = dot_lex();
					if (token != DOT_IS) {
						parser_error_string = "expected = at node regular";
						return (1);
					}
					token = dot_lex();
				} else if (strcmp(lastid, "root") == 0) {
					/* specify root of layout, not dot but circo */
					token = dot_lex();
					if (token != DOT_IS) {
						parser_error_string = "expected = at node root";
						return (1);
					}
					token = dot_lex();
					parser_error_string = "node root is not dot but circo";
					return (1);
				} else if (strcmp(lastid, "samplepoints") == 0) {
					/* number of point for a node at shape circle, ellipse, integer */
					token = dot_lex();
					if (token != DOT_IS) {
						parser_error_string = "expected = at node samplepoints";
						return (1);
					}
					token = dot_lex();
					if (token == DOT_STRING) {
						/* */
					} else if (token == DOT_NUM) {
						/* */
					} else {
						parser_error_string = "expected integer number at node samplepoints";
						return (1);
					}
				} else if (strcmp(lastid, "shape") == 0) {
					/* shape of node to draw */
					token = dot_lex();
					if (token != DOT_IS) {
						parser_error_string = "expected = at node shape";
						return (1);
					}
					token = dot_lex();
					if (token == DOT_ID) {
						tmps = lastid;
					} else if (token == DOT_STRING) {
						tmps = laststring;
					} else {
						parser_error_string = "expected id or string at node shape";
						return (1);
					}
					tmpi = is_a_dot_shape_name(tmps);
					/* error when not found */
					if (tmpi == 0) {
						parser_error_string = "unknown shape at node shape";
						return (1);
					}
					tmpshape_set = 1;
					/* adjust to shape */
					switch (tmpi) {
					case DSHAPE_ASSEMBLY:	/* "assembly" */
						tmpshape = 0;
						break;
					case DSHAPE_BOX:	/* "box" */
						tmpshape = NSHAPE_BOX;
						break;
					case DSHAPE_BOX3D:	/* "box3d" */
					case DSHAPE_CDS:	/* "cds" */
					case DSHAPE_CIRCLE:	/* "circle" */
					case DSHAPE_COMPONENT:	/* "component" */
					case DSHAPE_CYLINDER:	/* "cylinder" */
					case DSHAPE_DIAMOND:	/* "diamond" */
					case DSHAPE_DOUBLECIRCLE:	/* "doublecircle" */
					case DSHAPE_DOUBLEOCTAGON:	/* "doubleoctagon" */
					case DSHAPE_EGG:	/* "egg" */
					case DSHAPE_ELLIPSE:	/* "ellipse" */
					case DSHAPE_FIVEPOVERHANG:	/* "fivepoverhang" */
					case DSHAPE_FOLDER:	/* "folder" */
					case DSHAPE_HEXAGON:	/* "hexagon" */
					case DSHAPE_HOUSE:	/* "house" */
					case DSHAPE_INSULATOR:	/* "insulator" */
					case DSHAPE_INVHOUSE:	/* "invhouse" */
					case DSHAPE_INVTRAPEZIUM:	/* "invtrapezium" */
					case DSHAPE_INVTRIANGLE:	/* "invtriangle" */
					case DSHAPE_LARROW:	/* "larrow" */
					case DSHAPE_LPROMOTOR:	/* "lpromoter" */
					case DSHAPE_MCIRCLE:	/* "Mcircle" */
					case DSHAPE_MDIAMOND:	/* "Mdiamond" */
					case DSHAPE_MSQUARE:	/* "Msquare" */
					case DSHAPE_NONE:	/* "none" */
					case DSHAPE_NOTE:	/* "note" */
					case DSHAPE_NOVERHANG:	/* "noverhang" */
					case DSHAPE_OCTAGON:	/* "octagon" */
					case DSHAPE_OVAL:	/* "oval" */
					case DSHAPE_PARALLELOGRAM:	/* "parallelogram" */
					case DSHAPE_PENTAGON:	/* "pentagon" */
					case DSHAPE_PLAIN:	/* "plain" */
					case DSHAPE_PLAINTEXT:	/* "plaintext" */
					case DSHAPE_POINT:	/* "point" */
					case DSHAPE_POLYGON:	/* "polygon" */
					case DSHAPE_PRIMERSITE:	/* "primersite" */
					case DSHAPE_PROMOTER:	/* "promoter" */
					case DSHAPE_PROTEASESITE:	/* "proteasesite" */
					case DSHAPE_PROTEINSTAB:	/* "proteinstab" */
					case DSHAPE_RARROW:	/* "rarrow" */
						tmpshape = 0;
						break;
					case DSHAPE_RECORD:	/* special "record" */
						tmpshape = NSHAPE_RECORD;
						break;
					case DSHAPE_RECT:	/* "rect" */
					case DSHAPE_RECTANGLE:	/* "rectangle" */
					case DSHAPE_RESTRICTIONSITE:	/* "restrictionsite" */
					case DSHAPE_RIBOSITE:	/* "ribosite" */
					case DSHAPE_RNASTAB:	/* "rnastab" */
					case DSHAPE_RPROMOTER:	/* "rpromoter" */
					case DSHAPE_SEPTAGON:	/* "septagon" */
					case DSHAPE_SIGNATURE:	/* "signature" */
					case DSHAPE_SQUARE:	/* "square" */
					case DSHAPE_STAR:	/* "star" */
					case DSHAPE_TAB:	/* "tab" */
					case DSHAPE_TERMINATOR:	/* "terminator" */
					case DSHAPE_THREEPOVERHANG:	/* "threepoverhang" */
					case DSHAPE_TRAPEZIUM:	/* "trapezium" */
					case DSHAPE_TRIANGLE:	/* "triangle" */
					case DSHAPE_TRIPLEOCTAGON:	/* "tripleoctagon" */
					case DSHAPE_UNDERLINE:	/* "underline" */
					case DSHAPE_UTR:	/* "utr" */
					default:
						/* should not happen */
						tmpshape = 0;
						break;
					}
				} else if (strcmp(lastid, "shapefile") == 0) {
					/* shapefile="test.ps" in a box area is depreciated */
					token = dot_lex();
					if (token != DOT_IS) {
						parser_error_string = "expected = at node shapefile";
						return (1);
					}
					token = dot_lex();
					if (token == DOT_ID) {
						tmps = lastid;
					} else if (token == DOT_STRING) {
						tmps = laststring;
					} else {
						parser_error_string = "expected filename string at node shapefile";
						return (1);
					}
				} else if (strcmp(lastid, "showboxes") == 0) {
					/* print boxes, postscript only, value 0,1,2 */
					token = dot_lex();
					if (token != DOT_IS) {
						parser_error_string = "expected = at node showboxes";
						return (1);
					}
					token = dot_lex();
					if (token == DOT_STRING) {
						/* */
					} else if (token == DOT_NUM) {
						/* */
					} else {
						parser_error_string = "expected 0,1 or 2 at node showboxes";
						return (1);
					}
				} else if (strcmp(lastid, "sides") == 0) {
					/* number of sides if shape is polygon, int, default 4 */
					token = dot_lex();
					if (token != DOT_IS) {
						parser_error_string = "expected = at node sides";
						return (1);
					}
					token = dot_lex();
					if (token == DOT_STRING) {
						/* */
					} else if (token == DOT_NUM) {
						/* */
					} else {
						parser_error_string = "expected integer number at node sides";
						return (1);
					}
				} else if (strcmp(lastid, "skew") == 0) {
					/* skew factor for shape polygon, double -100..+100 */
					token = dot_lex();
					if (token != DOT_IS) {
						parser_error_string = "expected = at node skew";
						return (1);
					}
					token = dot_lex();
					if (token == DOT_STRING) {
						/* */
					} else if (token == DOT_NUM) {
						/* */
					} else {
						parser_error_string = "expected number at node skew";
						return (1);
					}
				} else if (strcmp(lastid, "sortv") == 0) {
					/* array packing order, int */
					token = dot_lex();
					if (token != DOT_IS) {
						parser_error_string = "expected = at node sortv";
						return (1);
					}
					token = dot_lex();
					if (token == DOT_STRING) {
						/* */
					} else if (token == DOT_NUM) {
						/* */
					} else {
						parser_error_string = "expected number at node sortv";
						return (1);
					}
				} else if (strcmp(lastid, "style") == 0) {
					/* style setting */
					/* style=item(,item)*
					 * style="" is same as no-style-argument
					 * can haven multiple params
					 * style does influence color,fillcolor
					 * style can be bold which is same as setpenwidth
					 * style for nodes is different from edges, clusters
					 * see docs, complicated. XXX
					 */
					token = dot_lex();
					if (token != DOT_IS) {
						parser_error_string = "expected = at node style";
						return (1);
					}
					token = dot_lex();
					/* style mode can be "" */
					if (token == DOT_STRING) {
						styles = laststring;
					} else if (token == DOT_ID) {
						styles = lastid;
					} else {
						parser_error_string = "expected style code at node style";
						return (1);
					}
					if (strlen(styles) == 0) {
						/* "" is same as no style parameter */
						style_set = 0;
					} else {
						/* parse here the components of the style string XXX fixme */
						style_set = 1;
						if (strchr(styles, ',') || strchr(styles, '(')) {
							/* assume to be be arg,arg or arg(arg) */
						} else {
							if (strcmp(styles, "filled") == 0) {
								stylefilled_set = 1;
							}
						}
					}
				} else if (strcmp(lastid, "target") == 0) {
					/* at url, svg only */
					token = dot_lex();
					if (token != DOT_IS) {
						parser_error_string = "expected '=' at node target";
						return (1);
					}
					token = dot_lex();
					/* can be string or escstring */
					if (token == DOT_STRING) {
						/* */
					} else if (token == DOT_ID) {
						/* */
					} else {
						parser_error_string = "expected string at node target";
						return (1);
					}
				} else if (strcmp(lastid, "tooltip") == 0) {
					/* tooltip svg only */
					token = dot_lex();
					if (token != DOT_IS) {
						parser_error_string = "expected '=' at node tooltip";
						return (1);
					}
					token = dot_lex();
					/* can be string or escstring */
					if (token == DOT_STRING) {
						/* */
					} else if (token == DOT_ID) {
						/* */
					} else {
						parser_error_string = "expected string at node tooltip";
						return (1);
					}
				} else if (strcmp(lastid, "vertices") == 0) {
					/* polygonal node vertices, arg is pointlist seperated by spaces */
					token = dot_lex();
					if (token != DOT_IS) {
						parser_error_string = "expected '=' at node vertices";
						return (1);
					}
					token = dot_lex();
					/* many tokens may follow */
				} else if (strcmp(lastid, "width") == 0) {
					/* min. witdth of node, double */
					token = dot_lex();
					if (token != DOT_IS) {
						parser_error_string = "expected '=' at node width";
						return (1);
					}
					token = dot_lex();
					if (token == DOT_STRING) {
						/* */
					} else if (token == DOT_NUM) {
						/* */
					} else {
						parser_error_string = "expected number at node width";
						return (1);
					}
				} else if (strcmp(lastid, "xlabel") == 0) {
					/* external label for node */
					token = dot_lex();
					if (token != DOT_IS) {
						parser_error_string = "expected '=' at node xlabel";
						return (1);
					}
					token = dot_lex();
					/* can be string or escstring */
					if (token == DOT_STRING) {
						/* */
					} else if (token == DOT_ID) {
						/* */
					} else {
						parser_error_string = "expected string at node xlabel";
						return (1);
					}
				} else if (strcmp(lastid, "xlp") == 0) {
					/* po. of ext. label in points */
					token = dot_lex();
					if (token != DOT_IS) {
						parser_error_string = "expected '=' at node xlp";
						return (1);
					}
					token = dot_lex();
					if (token == DOT_STRING) {
						/* */
					} else if (token == DOT_NUM) {
						/* */
					} else {
						parser_error_string = "expected point at node xlp";
						return (1);
					}
				} else {
					/* unknown lastid */
					token = dot_lex();
					if (token != DOT_IS) {
						parser_error_string = "expected '=' at unknown node attribute (1)";
						return (1);
					}
					token = dot_lex();
				}
				/* 1) end of node settings */
			} else {
				/* token not char not id */
				parser_error_string = "expected char or id at node attribute";
				return (1);
			}
		}		/* end of for() */
	}			/* end of if('] ') */
	else {
		/* not a [ so unlex */
		dot_unlex();
	}

	/*
	 * here at end of node options
	 */

	/* create the node or find existing */
	un = uniqnode(fname);

	if (option_parsedebug) {
		printf("%s(): nodename `%s' resulted in un %p\n", __FUNCTION__, fname, (void *)un);
		fflush(stdout);
	}

	/* did not exists then create */
	if (un == (struct unode *)0) {
		un = unode_new(fname);

		/* set defaults from "node[...]" */
		set_node_defaults(un, subg);

		/* default to white fillcolor */
		un->fillcolor = 0x00ffffff;

		un->name = fname;
		un->rootedon = subg;

		/* style=arg */
		if (style_set) {
			if (stylefilled_set) {
				un->bitflags2.stylefilled = 1;
			}
		}

		/* shape of node or record node */
		if (tmpshape_set) {
			un->bitflags.shape = tmpshape;
		}
		/* size of font to use */
		if (tmpfontsize) {
			un->fontsize = tmpfontsize;
		}
		/* name of font to use */
		if (tmpfontname) {
			un->fontname = tmpfontname;
		}
		/* color for text */
		un->textcolor = fcolor;
		/* url for node */
		if (tmpurl_set) {
			un->url = tmpurl;
		}

		if (flabel) {
			if (strlen(flabel) == 0) {
				labelset = 0;
				flabel = NULL;
			}
		}

		if (flabel == NULL) {
			/* could be a preset label */
			if (un->label) {
				flabel = un->label;
				labelset = un->labeltype;
			}
		}

		switch (labelset) {
		case 0:	/* no label */
			un->label = fname;
			break;
		case 1:	/* "" string */
			un->label = is_a_dot_escstring_node(flabel, un);
			if (un->label == NULL) {	/* parse error */
				myfree((void *)un, __FUNCTION__, __LINE__);
				return (1);
			}
			break;
		case 2:	/* id */
			un->label = flabel;
			break;
		case 3:	/* number */
			un->label = flabel;
			break;
		case 4:	/* html */
			un->label = is_a_dot_htmlstring_node(flabel, un);
			break;
		case 5:	/* char */
			un->label = flabel;
			break;
		default:	/* shouldnothappen */
			un->label = fname;
			break;
		}

		/* set as permanent dummy node is no label */
		if (un->label == NULL) {
			un->bitflags.dummynode = 1;
		}
		/* XXX fixme, node default style=filled is needed for filling with color or fillcolor, otherwise white fill */
		/* if fillcolor is not set, node is filled with color setting.
		 * if fillcolor is set, color is used for border color.
		 * this should handle the node preset too.
		 */
		if (stylefilled_set || un->bitflags2.stylefilled) {
			/* style=filled */
			if (bcolor_set && fillcolor_set) {
				un->fillcolor = fillcolor;
				un->bcolor = bcolor;
			} else if (bcolor_set) {
				un->fillcolor = bcolor;
				un->bcolor = bcolor;
			} else if (fillcolor_set) {
				un->fillcolor = fillcolor;
				un->bcolor = 0;	/* black */
			} else {
				un->fillcolor = 0x00d3d3d3;	/* light grey */
				un->bcolor = 0;	/* black */
			}
		} else {
			/* style is not filled */
			if (bcolor_set && fillcolor_set) {
				un->fillcolor = fillcolor;
				un->bcolor = bcolor;
			} else if (bcolor_set) {
				un->fillcolor = 0x00ffffff;	/* white */
				un->bcolor = bcolor;
			} else if (fillcolor_set) {
				un->fillcolor = 0x00ffffff;	/* white */
				un->bcolor = 0;	/* black */
			} else {
				/* use node default un->fillcolor, un->bcolor */
			}
		}

		/* color scheme */
		if (cscode_set) {
			un->bitflags.cscheme = cscode;
		}

		/* node is defined by node statement */
		un->bitflags.defbynode = 1;
		uniqnode_add(un->name, un);
		parsednl_add(un);

		if (option_parsedebug) {
			printf("%s(): created node `%s' shape=%d label `%s'\n", __FUNCTION__, un->name, un->bitflags.shape,
			       un->label);
			fflush(stdout);
		}
	} else {
		/*
		 * node is multiple times defined and use the first node definition.
		 * or node is only earlier created by edge statement.
		 */

		/* check if only defined by edge */
		if (un->bitflags.defbynode == 0 && un->bitflags.defbyedge == 1) {
			/* check if node is in different subgraph */
			if (un->rootedon != subg) {
				/* put node in current subgraph */
				un->rootedon = subg;
				/* node is defined by node statement */
				un->bitflags.defbynode = 1;
				/* node is relocated during parse */
				un->bitflags.reloc = 1;
			}
		}
		/* set defaults from "node[...]" */
		set_node_defaults(un, subg);

		/* style=arg */
		if (style_set) {
			if (stylefilled_set) {
				un->bitflags2.stylefilled = 1;
			}
		}

		/* shape of node to set before escstring_node() */
		if (tmpshape_set) {
			un->bitflags.shape = tmpshape;
		}

		if (tmpfontsize) {
			un->fontsize = tmpfontsize;
		}
		if (tmpfontname) {
			un->fontname = tmpfontname;
		}
		/* color for text */
		un->textcolor = fcolor;
		/* url for node */
		if (tmpurl_set) {
			un->url = tmpurl;
		}
		if (flabel) {
			if (strlen(flabel) == 0) {
				labelset = 0;
				flabel = NULL;
			}
		}

		if (flabel == NULL) {
			/* could be a preset label */
			if (un->label) {
				flabel = un->label;
				labelset = un->labeltype;
			}
		}

		switch (labelset) {
		case 0:	/* no label */
			un->label = fname;
			break;
		case 1:	/* "" string interpretation depends on node shape above */
			un->label = is_a_dot_escstring_node(flabel, un);
			if (un->label == NULL) {	/* parse error */
				return (1);
			}
			break;
		case 2:	/* id */
			un->label = flabel;
			break;
		case 3:	/* number */
			un->label = flabel;
			break;
		case 4:	/* html */
			un->label = is_a_dot_htmlstring_node(flabel, un);
			break;
		case 5:	/* char */
			un->label = flabel;
			break;
		default:	/* shouldnothappen */
			un->label = fname;
			break;
		}

		/* set as permanent dummy node if no label */
		if (un->label == NULL) {
			un->bitflags.dummynode = 1;
		}

		/* color scheme */
		if (cscode_set) {
			un->bitflags.cscheme = cscode;
		}

		/* if fillcolor is not set, node is filled with color setting.
		 * if fillcolor is set, color is used for border color.
		 * XXX see also comments above 
		 */

		if (stylefilled_set || un->bitflags2.stylefilled) {
			/* style=filled */
			if (bcolor_set && fillcolor_set) {
				un->fillcolor = fillcolor;
				un->bcolor = bcolor;
			} else if (bcolor_set) {
				un->fillcolor = bcolor;
				un->bcolor = bcolor;
			} else if (fillcolor_set) {
				un->fillcolor = fillcolor;
				un->bcolor = 0;	/* black */
			} else {
				un->fillcolor = 0x00d3d3d3;	/* light grey */
				un->bcolor = 0;	/* black */
			}
		} else {
			/* style is not filled */
			if (bcolor_set && fillcolor_set) {
				un->fillcolor = 0x00ffffff;	/* white */
				un->bcolor = bcolor;
			} else if (bcolor_set) {
				un->fillcolor = 0x00ffffff;	/* white */
				un->bcolor = bcolor;
			} else if (fillcolor_set) {
				un->fillcolor = 0x00ffffff;	/* white */
				un->bcolor = 0;	/* black */
			} else {
				/* use node default un->fillcolor, un->bcolor */
			}
		}

		/* node is defined by node statement */
		un->bitflags.defbynode = 1;
	}

	return (1);
}
